#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据分析模块
负责统计分析、图表生成、报告生成等功能
"""

from typing import List, Dict, Any, Tuple, Optional
from collections import Counter, defaultdict
from datetime import datetime
import statistics

from models import Student, Course, Grade, User
from utils import logger, performance_monitor
from exceptions import DataNotFoundError


class StatisticsCalculator:
    """统计计算器"""
    
    @staticmethod
    def calculate_student_statistics(students: List[Student]) -> Dict[str, Any]:
        """计算学生统计信息"""
        if not students:
            return {}
        
        ages = [s.age for s in students]
        majors = [s.major for s in students]
        classes = [s.class_name for s in students]
        
        return {
            'total_count': len(students),
            'age_stats': {
                'min': min(ages),
                'max': max(ages),
                'mean': statistics.mean(ages),
                'median': statistics.median(ages),
                'mode': statistics.mode(ages) if ages else 0
            },
            'major_distribution': dict(Counter(majors)),
            'class_distribution': dict(Counter(classes)),
            'majors_count': len(set(majors)),
            'classes_count': len(set(classes))
        }
    
    @staticmethod
    def calculate_grade_statistics(grades: List[Grade]) -> Dict[str, Any]:
        """计算成绩统计信息"""
        if not grades:
            return {}
        
        scores = [g.score for g in grades]
        
        # 基础统计
        stats = {
            'total_count': len(grades),
            'score_stats': {
                'min': min(scores),
                'max': max(scores),
                'mean': statistics.mean(scores),
                'median': statistics.median(scores),
                'std': statistics.stdev(scores) if len(scores) > 1 else 0
            }
        }
        
        # 等级分布
        grade_distribution = Counter(g.get_letter_grade() for g in grades)
        stats['grade_distribution'] = dict(grade_distribution)
        
        # 及格率
        pass_count = len([s for s in scores if s >= 60])
        stats['pass_rate'] = (pass_count / len(scores)) * 100 if scores else 0
        
        # 按学期统计
        semester_stats = defaultdict(list)
        for grade in grades:
            semester_stats[grade.semester].append(grade.score)
        
        stats['semester_stats'] = {
            semester: {
                'count': len(scores),
                'mean': statistics.mean(scores),
                'pass_rate': (len([s for s in scores if s >= 60]) / len(scores)) * 100
            }
            for semester, scores in semester_stats.items()
        }
        
        return stats
    
    @staticmethod
    def calculate_gpa_statistics(students: List[Student], grades: List[Grade], courses: List[Course]) -> Dict[str, Any]:
        """计算GPA统计信息"""
        if not students or not grades or not courses:
            return {}
        
        # 创建课程学分字典
        course_credits = {c.course_id: c.credits for c in courses}
        
        gpa_list = []
        student_gpas = {}
        
        for student in students:
            # 获取学生的期末考试成绩
            student_grades = [g for g in grades 
                            if g.student_id == student.student_id and g.exam_type == "期末考试"]
            
            if not student_grades:
                continue
            
            # 计算GPA
            total_grade_points = 0
            total_credits = 0
            
            for grade in student_grades:
                if grade.course_id in course_credits:
                    grade_point = grade.get_grade_point()
                    credits = course_credits[grade.course_id]
                    total_grade_points += grade_point * credits
                    total_credits += credits
            
            if total_credits > 0:
                gpa = total_grade_points / total_credits
                gpa_list.append(gpa)
                student_gpas[student.student_id] = {
                    'gpa': gpa,
                    'total_credits': total_credits,
                    'student_name': student.name
                }
        
        if not gpa_list:
            return {}
        
        return {
            'gpa_stats': {
                'min': min(gpa_list),
                'max': max(gpa_list),
                'mean': statistics.mean(gpa_list),
                'median': statistics.median(gpa_list),
                'std': statistics.stdev(gpa_list) if len(gpa_list) > 1 else 0
            },
            'student_gpas': student_gpas,
            'total_students_with_gpa': len(gpa_list)
        }


class ReportGenerator:
    """报告生成器"""
    
    def __init__(self, data_manager):
        self.data_manager = data_manager
    
    def generate_student_report(self, student_id: str) -> str:
        """生成学生个人报告"""
        student = self.data_manager.get_student(student_id)
        if not student:
            raise DataNotFoundError(f"学号 {student_id} 不存在", "student")
        
        grades = self.data_manager.search_grades(student_id=student_id)
        courses = self.data_manager.courses
        
        # 创建课程字典
        course_dict = {c.course_id: c for c in courses}
        
        report_lines = []
        report_lines.append("=" * 60)
        report_lines.append(f"学生成绩报告 - {student.name} ({student_id})")
        report_lines.append("=" * 60)
        report_lines.append(f"学号: {student.student_id}")
        report_lines.append(f"姓名: {student.name}")
        report_lines.append(f"专业: {student.major}")
        report_lines.append(f"班级: {student.class_name}")
        report_lines.append(f"报告生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        report_lines.append("=" * 60)
        
        if not grades:
            report_lines.append("该学生暂无成绩记录")
        else:
            # 按学期分组
            semester_grades = defaultdict(list)
            for grade in grades:
                semester_grades[grade.semester].append(grade)
            
            for semester in sorted(semester_grades.keys()):
                report_lines.append(f"\n{semester} 学期成绩:")
                report_lines.append("-" * 40)
                
                semester_gpa = 0
                total_credits = 0
                
                for grade in semester_grades[semester]:
                    course = course_dict.get(grade.course_id)
                    if course:
                        course_name = course.course_name
                        credits = course.credits
                        
                        report_lines.append(
                            f"  {course_name} ({grade.course_id}): {grade.score:.1f}分 "
                            f"({grade.get_letter_grade()}) 绩点:{grade.get_grade_point():.1f} "
                            f"学分:{credits:.1f} [{grade.exam_type}]"
                        )
                        
                        if grade.exam_type == "期末考试":
                            semester_gpa += grade.get_grade_point() * credits
                            total_credits += credits
                
                if total_credits > 0:
                    semester_gpa = semester_gpa / total_credits
                    report_lines.append(f"\n  {semester} 学期GPA: {semester_gpa:.3f}")
        
        report_lines.append("\n" + "=" * 60)
        
        return "\n".join(report_lines)
    
    def generate_course_report(self, course_id: str) -> str:
        """生成课程报告"""
        course = self.data_manager.get_course(course_id)
        if not course:
            raise DataNotFoundError(f"课程代码 {course_id} 不存在", "course")
        
        grades = self.data_manager.search_grades(course_id=course_id)
        students = self.data_manager.students
        
        # 创建学生字典
        student_dict = {s.student_id: s for s in students}
        
        report_lines = []
        report_lines.append("=" * 60)
        report_lines.append(f"课程成绩报告 - {course.course_name} ({course_id})")
        report_lines.append("=" * 60)
        report_lines.append(f"课程代码: {course.course_id}")
        report_lines.append(f"课程名称: {course.course_name}")
        report_lines.append(f"学分: {course.credits}")
        report_lines.append(f"授课教师: {course.teacher}")
        report_lines.append(f"报告生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        report_lines.append("=" * 60)
        
        if not grades:
            report_lines.append("该课程暂无成绩记录")
        else:
            scores = [g.score for g in grades]
            
            # 基础统计
            report_lines.append(f"总人数: {len(grades)}")
            report_lines.append(f"最高分: {max(scores):.1f}")
            report_lines.append(f"最低分: {min(scores):.1f}")
            report_lines.append(f"平均分: {statistics.mean(scores):.2f}")
            
            # 及格率
            pass_count = len([s for s in scores if s >= 60])
            pass_rate = (pass_count / len(scores)) * 100
            report_lines.append(f"及格人数: {pass_count}")
            report_lines.append(f"及格率: {pass_rate:.1f}%")
            
            # 等级分布
            grade_distribution = Counter(g.get_letter_grade() for g in grades)
            report_lines.append("\n等级分布:")
            for grade, count in sorted(grade_distribution.items()):
                percentage = (count / len(grades)) * 100
                report_lines.append(f"  {grade}: {count} 人 ({percentage:.1f}%)")
            
            # 成绩排名
            report_lines.append("\n成绩排名:")
            report_lines.append("-" * 40)
            sorted_grades = sorted(grades, key=lambda x: x.score, reverse=True)
            
            for i, grade in enumerate(sorted_grades, 1):
                student = student_dict.get(grade.student_id)
                student_name = student.name if student else "未知"
                report_lines.append(
                    f"{i:2d}. {student_name} ({grade.student_id}): {grade.score:.1f}分 "
                    f"({grade.get_letter_grade()}) 绩点:{grade.get_grade_point():.1f}"
                )
        
        report_lines.append("\n" + "=" * 60)
        
        return "\n".join(report_lines)
    
    def generate_system_report(self) -> str:
        """生成系统报告"""
        stats = self.data_manager.get_statistics()
        
        report_lines = []
        report_lines.append("=" * 60)
        report_lines.append("系统数据报告")
        report_lines.append("=" * 60)
        report_lines.append(f"报告生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        report_lines.append("=" * 60)
        
        # 基本统计
        report_lines.append(f"学生总数: {stats.get('students_count', 0)} 人")
        report_lines.append(f"课程总数: {stats.get('courses_count', 0)} 门")
        report_lines.append(f"成绩记录: {stats.get('grades_count', 0)} 条")
        report_lines.append(f"用户总数: {stats.get('users_count', 0)} 个")
        
        if 'age_range' in stats:
            age_min, age_max = stats['age_range']
            report_lines.append(f"年龄范围: {age_min} - {age_max} 岁")
            report_lines.append(f"平均年龄: {stats.get('average_age', 0):.1f} 岁")
        
        if 'majors_count' in stats:
            report_lines.append(f"专业数量: {stats['majors_count']} 个")
        
        if 'classes_count' in stats:
            report_lines.append(f"班级数量: {stats['classes_count']} 个")
        
        if 'total_credits' in stats:
            report_lines.append(f"课程总学分: {stats['total_credits']:.1f}")
        
        if 'semesters_count' in stats:
            report_lines.append(f"涉及学期: {stats['semesters_count']} 个")
        
        if 'exam_types_count' in stats:
            report_lines.append(f"考试类型: {stats['exam_types_count']} 种")
        
        report_lines.append("=" * 60)
        
        return "\n".join(report_lines)


class ChartGenerator:
    """图表生成器"""
    
    def __init__(self):
        self.matplotlib_available = self._check_matplotlib()
    
    def _check_matplotlib(self) -> bool:
        """检查matplotlib是否可用"""
        try:
            import matplotlib.pyplot as plt
            import matplotlib
            # 设置中文字体支持
            matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
            matplotlib.rcParams['axes.unicode_minus'] = False
            return True
        except ImportError:
            return False
    
    def create_major_distribution_chart(self, students: List[Student], save_path: str = None) -> bool:
        """创建专业分布图表"""
        if not self.matplotlib_available:
            logger.warning("matplotlib不可用，无法生成图表")
            return False
        
        try:
            import matplotlib.pyplot as plt
            
            # 统计专业数据
            major_count = Counter(s.major for s in students)
            majors = list(major_count.keys())
            counts = list(major_count.values())
            
            # 创建图表
            plt.figure(figsize=(12, 8))
            bars = plt.bar(range(len(majors)), counts, color='skyblue', edgecolor='navy', alpha=0.7)
            
            # 设置标题和标签
            plt.title('专业分布统计', fontsize=16, fontweight='bold')
            plt.xlabel('专业', fontsize=12)
            plt.ylabel('学生人数', fontsize=12)
            
            # 设置x轴标签
            plt.xticks(range(len(majors)), majors, rotation=45, ha='right')
            
            # 在柱子上显示数值
            for i, bar in enumerate(bars):
                height = bar.get_height()
                plt.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                        f'{int(height)}', ha='center', va='bottom')
            
            # 调整布局
            plt.tight_layout()
            
            # 保存图表
            if save_path:
                plt.savefig(save_path, dpi=300, bbox_inches='tight')
                logger.info(f"图表已保存: {save_path}")
            
            plt.show()
            return True
            
        except Exception as e:
            logger.error(f"生成专业分布图表失败: {e}")
            return False
    
    def create_age_distribution_chart(self, students: List[Student], save_path: str = None) -> bool:
        """创建年龄分布图表"""
        if not self.matplotlib_available:
            logger.warning("matplotlib不可用，无法生成图表")
            return False
        
        try:
            import matplotlib.pyplot as plt
            
            # 统计年龄数据
            age_count = Counter(s.age for s in students)
            ages = sorted(age_count.keys())
            counts = [age_count[age] for age in ages]
            
            # 创建图表
            plt.figure(figsize=(10, 6))
            bars = plt.bar(ages, counts, color='orange', edgecolor='darkorange', alpha=0.7)
            
            # 设置标题和标签
            plt.title('年龄分布柱状图', fontsize=16, fontweight='bold')
            plt.xlabel('年龄', fontsize=12)
            plt.ylabel('学生人数', fontsize=12)
            
            # 在柱子上显示数值
            for bar in bars:
                height = bar.get_height()
                plt.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                        f'{int(height)}', ha='center', va='bottom')
            
            # 调整布局
            plt.tight_layout()
            
            # 保存图表
            if save_path:
                plt.savefig(save_path, dpi=300, bbox_inches='tight')
                logger.info(f"图表已保存: {save_path}")
            
            plt.show()
            return True
            
        except Exception as e:
            logger.error(f"生成年龄分布图表失败: {e}")
            return False
    
    def create_grade_distribution_chart(self, grades: List[Grade], save_path: str = None) -> bool:
        """创建成绩分布图表"""
        if not self.matplotlib_available:
            logger.warning("matplotlib不可用，无法生成图表")
            return False
        
        try:
            import matplotlib.pyplot as plt
            
            # 统计成绩数据
            scores = [g.score for g in grades]
            
            # 创建直方图
            plt.figure(figsize=(10, 6))
            plt.hist(scores, bins=20, color='lightgreen', edgecolor='darkgreen', alpha=0.7)
            
            # 设置标题和标签
            plt.title('成绩分布直方图', fontsize=16, fontweight='bold')
            plt.xlabel('成绩', fontsize=12)
            plt.ylabel('人数', fontsize=12)
            
            # 添加统计信息
            mean_score = statistics.mean(scores)
            plt.axvline(mean_score, color='red', linestyle='--', label=f'平均分: {mean_score:.1f}')
            plt.legend()
            
            # 调整布局
            plt.tight_layout()
            
            # 保存图表
            if save_path:
                plt.savefig(save_path, dpi=300, bbox_inches='tight')
                logger.info(f"图表已保存: {save_path}")
            
            plt.show()
            return True
            
        except Exception as e:
            logger.error(f"生成成绩分布图表失败: {e}")
            return False
